 /*
    *  Copyright 2004 Thevert Liu  <thevert.liu@gmail.com>
    *
    *  Licensed under the Apache License, Version 2.0 (the "License");
    *  you may not use this file except in compliance with the License.
    *  You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
   *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
#ifndef X2_XMLSHELL_H
#define X2_XMLSHELL_H

#include <iostream>
#include <string>
#include <sstream>
#include <list>
#include <vector>
#include <TypeTrans.h>
#include <StrValueCache.h>

#include <handler.h>


template<typename FIRST, typename SECOND>
class P2PMap : public std::list<std::pair<FIRST, SECOND> >
{
public:
    typedef std::list<std::pair<FIRST, SECOND> > PList ;
    
    typedef typename PList::iterator       iterator ;
    typedef typename PList::const_iterator const_iterator ;
    
    bool push(const FIRST& f, const SECOND& s) {
        std::pair<FIRST, SECOND> p ;
        p.first = f;
        p.second = s ;
        push_back(p) ;
    }
    
    bool getSecond(const FIRST& f, SECOND& s) const {
        const_iterator it = PList::begin();
        for (;it!=PList::end();it++) {
            if ((*it).first == f) {
                s = (*it).second ;
                return true ;
            }
        }
        return false ;
    }
    
    bool getFirst(const SECOND& s, FIRST& f) const  {
        const_iterator it = PList::begin();
        for (;it!=PList::end();it++) {
            if ((*it).second == s) {
                f = (*it).first ;
                return true ;
            }
        }
        return false ;
    }

} ;


class XMLContext : public StrValueCache
{
public:
    void registNS (const std::string& prefix, const std::string& ns) ;
    bool getPrefix(const std::string& ns, std::string& prefix) const ;
    bool getNS(const std::string& prefix, std::string& ns) const ;
    bool empty() const {return mNamespaces.empty();}
    bool write(std::ostream& os) const ;
private:
    P2PMap<std::string, std::string> mNamespaces ;
} ;


class IXMLShell ;

class IXMLElement 
{
public:
    IXMLElement(const std::string& tagName) : mTagName(tagName){}
    virtual ~IXMLElement() {}
    
    virtual void regist(IXMLShell& shell) = 0 ;
    
    std::string getTagName() const {return mTagName;}
    
protected:
    std::string mTagName ;    
} ;

class IShellAttribute
{
public:
    virtual ~IShellAttribute(){}
	virtual bool empty() = 0 ;
	virtual bool required() = 0 ;
    virtual bool set(const std::string& v) = 0 ;
    virtual bool get(std::string& v) = 0 ;
} ;

class IShellElement
{
public:
    virtual ~IShellElement() {}
    virtual bool create(const std::string& tagName, IXMLShell& shell) = 0 ;
	virtual bool empty() = 0 ;
	virtual bool isWell() = 0 ;
	virtual void access(IParamHandler<IXMLElement*>* handler) = 0 ;
//    virtual bool toXML(std::ostream& os) = 0 ;
} ;

class IValue 
{
public:
    virtual bool setValue(const std::string& value) = 0 ;
    virtual bool getValue(std::string& value)  = 0 ;
} ;

class IExtraBlock 
{
public:
    virtual bool setData(const std::string& data) = 0 ;
    virtual bool getData(std::string& data)  = 0 ;
} ;


class IXMLShell
{
public:
    struct AttributeNode {
        AttributeNode(const std::string& p, const std::string& n, IShellAttribute* a)
        : prefix(p), name(n), adapter(a) 
        {}
        std::string prefix ;
        std::string name ;
        IShellAttribute* adapter ;
    } ;
    
    struct ElementNode {
        ElementNode(const std::string& p, const std::string& n, IShellElement* a)
        : prefix(p), name(n), adapter(a) 
        {}
        std::string prefix ;
        std::string name ;
        IShellElement *  adapter ;
    } ;
    
    virtual ~IXMLShell() {};    

    virtual bool registAttribute(const std::string& name, IShellAttribute* attr) = 0 ;
    virtual bool registAttributeNS(const std::string& ns, const std::string& name, IShellAttribute* attr) = 0 ;
    virtual bool registElement(const std::string& name, IShellElement* element) = 0 ;
    virtual bool registElementNS(const std::string& ns, const std::string& name, IShellElement* element) = 0 ;
    virtual bool registValue(IValue* value) = 0 ;
    virtual bool registExtraBlock(IExtraBlock * value) = 0 ;

    virtual IExtraBlock* getExtraBlock() = 0 ;
    virtual IValue*      getValue() = 0 ;
    virtual IShellAttribute*  getAttribute(const std::string& name) = 0 ;
    virtual IShellAttribute*  getAttributeNS(const std::string& ns, const std::string& name) = 0 ;
    virtual IShellElement*    getElement(const std::string& tagName) = 0 ;
    virtual IShellElement*    getElementNS(const std::string& ns, const std::string& tagName) = 0 ;

    virtual bool browse(IParamHandler<AttributeNode&>* handler) = 0 ;

    virtual bool browse(IParamHandler<ElementNode&>*  handler) = 0 ;
    
    virtual bool isWell() const  = 0 ;
    
    virtual bool isEmpty() const = 0 ;
    
} ;


class XMLShell : public IXMLShell
{
public:
    XMLShell() : mValue(NULL), mExtraBlock(NULL) {}
    virtual ~XMLShell() ;
    virtual bool registAttributeNS(const std::string& ns, const std::string& name, IShellAttribute* attr) ;
    virtual bool registElementNS(const std::string& ns, const std::string& name, IShellElement* element) ;
    virtual bool registAttribute(const std::string& name, IShellAttribute* attr) ;
    virtual bool registElement(const std::string& name, IShellElement* element) ;
    virtual bool registValue(IValue* value) ;
    virtual bool registExtraBlock(IExtraBlock * value) ;

    virtual IExtraBlock*      getExtraBlock() ;
    
    virtual IValue*           getValue() ;
    
    virtual IShellAttribute*  getAttribute(const std::string& name) ;
    
    virtual IShellAttribute*  getAttributeNS(const std::string& ns, const std::string& name) ;
    
    virtual IShellElement*    getElement(const std::string& tagName) ;
    
    virtual IShellElement*    getElementNS(const std::string& ns, const std::string& tagName) ;

    virtual bool browse(IParamHandler<IXMLShell::AttributeNode&>* handler) ;

    virtual bool browse(IParamHandler<IXMLShell::ElementNode&>*  handler) ;
	
	virtual bool isWell() const {return true;} /// TODO: check every attribute and element.

    virtual bool isEmpty() const  ;
	
private:    
    typedef std::list<void*>  VList ;
    VList mAttributes ; // use void * to decrease the type of std::list. 
    VList mElements ;   // use void * to decrease the type of std::list.
    
    IValue*      mValue ;
    IExtraBlock* mExtraBlock ;
} ;




template<typename T>
class TContainer
{
public:
    typedef T** iterator ;
	typedef const T** const_iterator ;

    TContainer() {};
    ~TContainer() 
    {
        clear() ;
    }
	
	
	void clear() {
	    for (size_t i=0; i<mItems.size(); i++) {
		    delete (T*)mItems[i] ;
		}
		mItems.clear() ;
	}
	
	size_t size() {
	    return mItems.size() ;
	}
	
	T* at(size_t i) {
	    if (i>=size()) {
		    return NULL ;
		}
		return static_cast<T*> (mItems[i]) ;
	}

	T* operator[](size_t i) {
	    return at(i) ;
	}

	void erase(size_t i) {
	    if (i<size()) {
		    delete at(i) ;
			mItems.erase(mItems.begin()+i) ;
		}
	}
	
	void push(T* t) {
	    mItems.push_back((void*)t) ;
	}
	
	void insert(size_t pos, T* t) {
	    mItems.insert(pos, 1, (void*)t) ;
	}
	
private:
	std::vector<void*> mItems ;
} ;



/// class IParam
///    This is the interface class for param set and get operation.
/// Params:
///   TS type for set function.
///   TG type for get function.
///   
template<typename TS, typename TG>
class IParam
{
public:
    virtual ~IParam() {}
    virtual void set(TS t) = 0 ;
    virtual TG get() = 0 ;
} ;

template<typename O, typename T>
class TGet
{
public:
    typedef T(O::*Get)() ;
    typedef Get GetFunc ;
} ;

template<typename O, typename T>
class TSet
{
public:
    typedef void (O::*Set)(T) ;
    typedef Set SetFunc ;
} ;

/// class TParam
///    This is the interface class for param set and get operation.
/// Params:
///   Obj type of class type of the object which take this parameter in.
///   TS type for set function.
///   TG type for get function.
///   
template<class Obj, class TS, class TG>
class TParam : public IParam<TS, TG>
{
public:
    typedef typename TSet<Obj, TS>::Set SetFunc ;
    typedef typename TGet<Obj, TG>::Get GetFunc ;
    
    TParam(Obj* obj, SetFunc setfunc, GetFunc getfunc) 
        : mObj(obj), mSet(setfunc), mGet(getfunc) 
    {
    } 
    
    void set(TS t) {(mObj->*mSet)(t);}

    TG get() {return (mObj->*mGet)();}
    
private:
    Obj *mObj ;
    SetFunc mSet ;
    GetFunc mGet ;    
} ;




template<typename T>
class TAttribute : public IShellAttribute
{
public:
    TAttribute(IParam<const T&, T*>* p, bool require) 
        : mParam(p) , mRequired(require) 
    {
    }
    
    template <typename O> 
    TAttribute(O* obj, void (O::*s)(const T&), T* (O::*g)(), bool require=false)    
        : mParam ( new TParam<O,const T&, T*>(obj, s, g)), mRequired(require)
    {
    }
    
    
    ~TAttribute(){
        delete mParam ;
    }
    
    bool set(const std::string& s) {
        T t ;
        if (!TypeTrans<T>::S2V(s, t)) {
            return false ;
        }
        mParam->set(t) ;
        return true ;
    }
    
    bool get(std::string& s)  {
        T* t = mParam->get() ;
        if (!t) {
            log  << "Not find value"  ;
            return false ;
        }

        if (!TypeTrans<T>::V2S(*t, s)) {
            log << "Trans val failed"  ;
            return false ;
        }
        return true ;
    }
	
	bool empty() {
	    return (mParam->get() == NULL) ;
	}
	
	bool required()  {return mRequired;}
	
    
private:
    IParam<const T&, T*> *mParam ;    
    
    bool mRequired ;
} ;

template<typename T>
class TElement : public IShellElement
{
public:
    
    TElement(IParam<T*, T*> *param, bool required=false)
	  : mParam(param), mRequired(required)
	{
	}
    
    template<typename O>
    TElement(O* obj, typename TParam<O, T*, T*>::SetFunc s, typename TParam<O, T*, T*>::GetFunc g, bool r) 
      : mParam(new TParam<O, T*, T*>(obj, s, g)), mRequired(r)
    {}
      
	virtual ~TElement() {
	    delete mParam ;
	}
	
    virtual bool create(const std::string& name, IXMLShell& shell) {
		T* t = new T(name) ;
		t->regist(shell) ;
		mParam->set(t) ;
		return true ;
	}
	
	virtual bool empty() {
		return (mParam->get() == NULL) ;
	}
	
	
	virtual bool isWell() {
	    return !(require() && empty()) ;
	}
	
	virtual void access(IParamHandler<IXMLElement*>* handler) {
        handler->handle(mParam->get()) ;
    }

private:
	bool require() const {return mRequired;}
    IParam<T*, T*>* mParam ;
    bool mRequired ;

} ;

template<typename T>
class TElements : public IShellElement
{
public:

    TElements(IParam<T*, TContainer<T>& >* param, size_t nmin, size_t nmax) 
	  : mParam(param), mMin(nmin), mMax(nmax)
	{
    }

    template<typename O> 
    TElements(O* obj, void (O::*s)(T*) , 
               TContainer<T>&(O::*g)(), 
               size_t mi, 
               size_t ma)
      : mParam(new TParam<O, T*, TContainer<T>&>(obj, s, g)), mMin(mi), mMax(ma)
    {
       
    } 
	
	virtual ~TElements() {delete mParam;} 

	
    virtual bool create(const std::string& name, IXMLShell& shell) {
	    T* t = new T(name) ;
		if (!t) return false ;
		t->regist(shell) ;
		mParam->set(t) ;
	}

	virtual void access(IParamHandler<IXMLElement*>* handler) {
        TContainer<T>& elems = mParam->get() ;
        for (size_t i=0; i<elems.size(); i++) {
            handler->handle(elems[i]) ;
        }
    }

    virtual bool empty() {
	    return (mParam->get().size()==0) ;
	}

	virtual bool isWell() {
	    size_t s = mParam->get().size() ;
		return (s>=mMin && s<=mMax) ;
	}

private:	
	IParam<T*, TContainer<T>& >* mParam ;
	int mMin ;
	int mMax ;
	
} ;


template<typename T>
class TValue : public IValue
{
public:
    template <typename O> TValue(O* obj, void (O::*s)(const T&), T* (O::*g)())
       : mParam(new TParam<O, const T&, T*>(obj, s, g))
    {
    }
    
    ~TValue() {delete mParam;}
    
    bool setValue(const std::string& s) {
        T t ;
        if (!TypeTrans<T>::S2V(s, t)) {
            return false ;
        }
        mParam->set(t) ;
        return true ;
    }
    
    bool getValue(std::string& s)  {
        T* t = mParam->get() ;
        if (!t) {
            s3crit  << "Not find value" << ENDL ;
            return false ;
        }

        if (!TypeTrans<T>::V2S(*t, s)) {
            s3crit << "Trans val failed" << ENDL ;
            return false ;
        }
        return true ;
    }
private:    
    IParam<const T&, T*> *mParam ;
} ;


template<> class TValue<std::string> : public IValue
{
public:
    template <typename O> TValue(O* obj, void (O::*s)(const std::string&), std::string* (O::*g)())
       : mParam(new TParam<O, const std::string&, std::string*>(obj, s, g))
    {
    }

    ~TValue() {delete mParam;}
    
    bool setValue(const std::string& s) {
        mParam->set(s) ;
        return true ;
    }
    
    bool getValue(std::string& s)  {
        std::string* t = mParam->get() ;
        if (!t) {
            s3crit  << "Not find value" << ENDL ;
            return false ;
        }
        s = *t ;
        return true ;
    }   
    
private:
    IParam<const std::string&, std::string*> *mParam ;
} ;


class ExtraBlock : public IExtraBlock 
{
public:
    template <typename O> 
    ExtraBlock(O* obj, 
        typename TParam<O, const std::string&, std::string*>::SetFunc s, 
        typename TParam<O, const std::string&, std::string*>::GetFunc g)
      : mParam(new TParam<O, const std::string&, std::string*>(obj, s, g))
    {
    }
    
    ~ExtraBlock() {delete mParam;}
    
    bool setData(const std::string& data)  {mParam->set(data);return true;}
    bool getData(std::string& data) {
        std::string* t = mParam->get() ;
        if (t==NULL) return false ;
        data = *t ;
        return true ;
    }  ;
    
    IParam<const std::string&, std::string*> * mParam ;
};

template<typename O, typename TS, typename TG>
void CreateParam(O* obj, void (O::*s)(TS) , TG (O::*g)() )
{
    return TParam<O, TS, TG> (obj, s, g) ;
} 


class Shell 
{
public:
    Shell(IXMLShell& shell) : sh(shell){}

template<typename O, typename T>
bool regValue
         (
           O* obj,
          void (O::*s)(const T&),
          T* (O::*g)(void)
//           typename TSet<O, const T&>::SetFunc s,
//           typename TGet<O, T*>::GetFunc g
         )
{
    return sh.registValue(new TValue<T>(obj, s, g)) ;
}


template<typename O, typename T> 
bool regAttribute
        ( 
          const std::string& name, 
          O* obj, 
          void (O::*s)(const T&),
          T* (O::*g)(void),
          //typename TSet<O, const T&>::Set s,
          //typename TGet<O, T*>::Get g,
          bool require=false
        )
{
    return sh.registAttribute(name, new TAttribute<T>(obj, s, g, require)) ;
}

template<typename O, typename T>
bool regElement
            (
              const std::string& name, 
              O* obj,
              typename TParam<O, T*, T*>::SetFunc s,
              typename TParam<O, T*, T*>::GetFunc g,
              bool required=false
            )
{
    return sh.registElement(name, new TElement<T>(obj, s, g, required)) ;
}

template<typename O, typename T>
bool regElement
            (
              const std::string& name, 
              O* obj,
              typename TParam<O, T*, TContainer<T>&>::SetFunc s,
              typename TParam<O, T*, TContainer<T>&>::GetFunc g,
              size_t mi = 0,
              size_t ma = (size_t)-1
            )
{
    return sh.registElement(name, new TElements<T>(obj, s, g, mi, ma)) ;
}




private:

IXMLShell& sh ;

} ;


#endif


